package org.erp.store.strategy.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.erp.common.enums.DeviceInventoryEvent;
import org.erp.common.enums.ProductEvent;
import org.erp.store.domain.entity.DeviceInventory;
import org.erp.store.domain.dto.BatchStatusMqMessageDto;
import org.erp.store.domain.dto.BatchDeviceInventoryDto;
import org.erp.store.domain.dto.BatchDeviceInventoryResponseDto;
import org.erp.store.domain.dto.InventoryDto;
import org.erp.store.infrastructure.enums.DeviceInventoryState;
import org.erp.store.infrastructure.mq.service.EventNotificationMqService;
import org.erp.store.infrastructure.event.InventoryLogEvent;
import org.erp.store.infrastructure.event.ProductCallbackEvent;
import org.erp.store.strategy.hook.AfterPersistHook;
import org.erp.store.service.DeviceInventoryService;
import org.erp.store.infrastructure.config.InventoryStateTransitionManager;
import org.erp.store.strategy.InventoryEventStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@Slf4j
public abstract class BaseInventoryEventStrategy implements InventoryEventStrategy {

    @Resource(name = "defaultAfterPersistHook")
    protected AfterPersistHook defaultAfterPersistHook;

    @Resource(name = "mqAndLogAfterPersistHook")
    protected AfterPersistHook mqAndLogAfterPersistHook;

    @Resource
    protected DeviceInventoryService deviceInventoryService;

    @Resource
    protected InventoryStateTransitionManager stateTransitionManager;

  
    protected final BatchDeviceInventoryResponseDto batchUpdate(BatchDeviceInventoryDto dto, BiConsumer<DeviceInventory, InventoryDto> customizer, AfterPersistHook afterPersist) {
        Assert.isTrue(dto.getInventoryList() != null && !dto.getInventoryList().isEmpty(), "库存列表不能为空");
        List<Long> inventoryIds = dto.getInventoryList().stream().map(InventoryDto::getInventoryId).collect(Collectors.toList());
        long startTime1 = System.currentTimeMillis();
        List<DeviceInventory> inventories = deviceInventoryService.selectByIdsWithStatus(inventoryIds);
        long endTime1 = System.currentTimeMillis();
        log.info("批量查询库存耗时：{}ms", endTime1 - startTime1 );
        Assert.isTrue(inventories != null && !inventories.isEmpty(), "无效的库存ID");
        Map<Long, InventoryDto> dtoMap = dto.getInventoryList().stream().collect(Collectors.toMap(InventoryDto::getInventoryId, x -> x));
        Map<DeviceInventoryState, DeviceInventoryState> transitions = stateTransitionManager.getEventTransitions(dto.getTargetEvent());
        Map<String, Integer> originalStateMap = new HashMap<>(inventories.size() * 2);
        List<DeviceInventory> updateList = new ArrayList<>(inventories.size());
        long startTime2 = System.currentTimeMillis();
        for (DeviceInventory inv : inventories) {
            InventoryDto invDto = dtoMap.get(inv.getId());
            if (invDto == null) {
                continue;
            }
            DeviceInventoryState originalState = DeviceInventoryState.getByCode(inv.getStatus());
            DeviceInventoryState targetState = transitions.get(originalState);
            if (targetState == null) {
                continue;
            }
            originalStateMap.put(inv.getProductCode(), inv.getStatus());
            inv.setStatus(targetState.getCode());
            inv.setOperateId(dto.getOperateId());
            inv.setOperateName(dto.getOperateName());
            updateInboundTiming(inv, originalState, targetState);
            if (customizer != null) {
                customizer.accept(inv, invDto);
            }
            updateList.add(inv);
        }
        long endTime2 = System.currentTimeMillis();
        log.info("批量更新库存遍历耗时：{}ms", endTime2 - startTime2 );
        if (!updateList.isEmpty()) {
            long startTime3 = System.currentTimeMillis();
            int updateCount = deviceInventoryService.updateBatchByIds(updateList);
            long endTime3 = System.currentTimeMillis();
            log.info("批量更新库存数据库耗时：{}ms", endTime3 - startTime3 );
            if (updateCount > 0 && afterPersist != null) {
                afterPersist.accept(dto, updateList, originalStateMap);
            }
        }
        BatchDeviceInventoryResponseDto response = new BatchDeviceInventoryResponseDto();
        response.setTotalCount(dto.getInventoryList().size());
        response.setSuccessCount(updateList.size());
        response.setFailedCount(response.getTotalCount() - response.getSuccessCount());
        response.setTargetEvent(dto.getTargetEvent());
        return response;
    }


    /**
     * 更新在库状态、在库天数
     *
     */
    private void updateInboundTiming(DeviceInventory inv, DeviceInventoryState originalState, DeviceInventoryState targetState) {
        Date now = new Date();
        boolean enteringInbound = originalState != DeviceInventoryState.INBOUND && targetState == DeviceInventoryState.INBOUND;
        boolean exitingInbound = originalState == DeviceInventoryState.INBOUND && targetState != DeviceInventoryState.INBOUND;
        if (enteringInbound) {
            inv.setLastInboundTime(now);
        } else if (exitingInbound) {
            int delta = (int) DateUtil.between(inv.getLastInboundTime(), now, DateUnit.DAY);
            int base = inv.getStockDaysTotal() == null ? 0 : inv.getStockDaysTotal();
            inv.setStockDaysTotal(base + delta);
            inv.setLastInboundTime(null);
        }
    }


}
